package cn.initcap.algorithm.swing.maze.bfs;

import cn.initcap.algorithm.swing.AlgoVisHelper;
import cn.initcap.algorithm.swing.BaseAlgoFrame;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.util.LinkedList;

/**
 * @author initcap
 * @date 8/1/19 10:45 AM
 */
public class AlgoVisualizer {

    private static final int[][] D = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}};
    private static int DELAY = 5;
    private static int blockSide = 8;
    private MazeData data;
    private BaseAlgoFrame frame;

    public AlgoVisualizer(String mazeFile) {

        // 初始化数据
        data = new MazeData(mazeFile);
        int sceneHeight = data.N() * blockSide;
        int sceneWidth = data.M() * blockSide;

        // 初始化视图
        EventQueue.invokeLater(() -> {
            frame = new BaseAlgoFrame("Maze Solver Visualization", sceneWidth, sceneHeight) {
                @Override
                public void draw(Graphics graphics) {
                    Graphics2D g2d = (Graphics2D) graphics;
                    int w = sceneWidth / data.M();
                    int h = sceneHeight / data.N();

                    for (int i = 0; i < data.N(); i++) {
                        for (int j = 0; j < data.M(); j++) {
                            if (data.getMaze(i, j) == MazeData.WALL) {
                                AlgoVisHelper.setColor(g2d, AlgoVisHelper.LIGHT_BLUE);
                            } else {
                                AlgoVisHelper.setColor(g2d, AlgoVisHelper.WHITE);
                            }

                            if (data.path[i][j]) {
                                AlgoVisHelper.setColor(g2d, AlgoVisHelper.YELLOW);
                            }

                            if (data.result[i][j]) {
                                AlgoVisHelper.setColor(g2d, AlgoVisHelper.RED);
                            }

                            AlgoVisHelper.fillRectangle(g2d, j * w, i * h, w, h);
                        }
                    }
                }
            };

            new Thread(() -> {
                run();
            }).start();
        });
    }

    public static void main(String[] args) {

        String mazeFile = "algorithm/maze_101_101.txt";

        new AlgoVisualizer(mazeFile);
    }

    private void run() {

        setData(-1, -1, false);

        LinkedList<Position> queue = new LinkedList<Position>();
        Position entrance = new Position(data.getEntranceX(), data.getEntranceY());
        queue.addLast(entrance);
        data.visited[entrance.getX()][entrance.getY()] = true;

        boolean isSolved = false;

        while (queue.size() != 0) {
            Position curPos = queue.pop();
            setData(curPos.getX(), curPos.getY(), true);

            if (curPos.getX() == data.getExitX() && curPos.getY() == data.getExitY()) {
                isSolved = true;
                findPath(curPos);
                break;
            }

            for (int i = 0; i < 4; i++) {
                int newX = curPos.getX() + D[i][0];
                int newY = curPos.getY() + D[i][1];

                if (data.inArea(newX, newY)
                        && !data.visited[newX][newY]
                        && data.getMaze(newX, newY) == MazeData.ROAD) {
                    queue.addLast(new Position(newX, newY, curPos));
                    data.visited[newX][newY] = true;
                }
            }

        }

        if (!isSolved) {
            System.out.println("The maze has no Solution!");
        }

        setData(-1, -1, false);
    }

    private void findPath(Position des) {

        Position cur = des;
        while (cur != null) {
            data.result[cur.getX()][cur.getY()] = true;
            cur = cur.getPrev();
        }
    }

    private void setData(int x, int y, boolean isPath) {
        if (data.inArea(x, y)) {
            data.path[x][y] = isPath;
        }

        frame.render(data);
        AlgoVisHelper.pause(DELAY);
    }
}